#pragma once

#include "../umlmodel_global.h"

#include <memory>
#include <vector>

#include "Namespace.h"
#include "PackageableElement.h"
#include "TemplateableElement.h"

using std::make_shared;
using std::shared_ptr;
using std::vector;

namespace uml {
class NamedElement;
class PackageMerge;
class PackageableElement;
class Profile;
class ProfileApplication;
class Stereotype;
class Type;

/// <summary>
/// Package
/// A package can have one or more profile applications to indicate which profiles
/// have been applied. Because a profile is a package, it is possible to apply a
/// profile not only to packages, but also to profiles.
/// Package specializes
/// TemplateableElement and PackageableElement specializes ParameterableElement to
/// specify that a package can be used as a template and a PackageableElement as a
/// template parameter.
/// A package is used to group elements, and provides a
/// namespace for the grouped elements.
/// </summary>
class UMLMODEL_EXPORT Package : public TemplateableElement, public Namespace, public PackageableElement {

public: // Constructors and destructors
    explicit Package(const QString& umlType="uml::Package");

public: // JsonSerializable override
    void fromJson(const QJsonObject& json, QStringList& errorList) override;
    void toJson(QJsonObject& json) override;

public: // Methods
    /// <summary>
    /// allApplicableStereotypes
    /// The query allApplicableStereotypes() returns all the directly or indirectly
    /// owned stereotypes, including stereotypes contained in sub-profiles.
    /// </summary>
    virtual shared_ptr<vector<Stereotype>> allApplicableStereotypes() const;

    /// <summary>
    /// containingProfile
    /// The query containingProfile() returns the closest profile directly or indirectly
    /// containing this package (or this package itself, if it is a profile).
    /// </summary>
    virtual shared_ptr<Profile> containingProfile() const;

    /// <summary>
    /// makesVisible
    /// The query makesVisible() defines whether a Package makes an element visible
    /// outside itself. Elements with no visibility and elements with public visibility
    /// are made visible.
    /// </summary>
    virtual bool makesVisible(shared_ptr<NamedElement> el) const;

    /// <summary>
    /// mustBeOwned
    /// The query mustBeOwned() indicates whether elements of this type must have an
    /// owner.
    /// </summary>
    bool mustBeOwned() const override;

    /// <summary>
    /// visibleMembers
    /// The query visibleMembers() defines which members of a Package can be accessed
    /// outside it.
    /// </summary>
    virtual shared_ptr<vector<PackageableElement>> visibleMembers() const;

public: // Properties
    /// <summary>
    /// URI getter
    /// Provides an identifier for the package that can be used for many purposes. A URI
    /// is the universally unique identification of the package following the IETF URI
    /// specification, RFC 2396 http://www.ietf.org/rfc/rfc2396.txt and it must comply
    /// with those syntax rules.
    /// </summary>
    QString URI() const;

    /// <summary>
    /// URI setter
    /// Provides an identifier for the package that can be used for many purposes. A URI
    /// is the universally unique identification of the package following the IETF URI
    /// specification, RFC 2396 http://www.ietf.org/rfc/rfc2396.txt and it must comply
    /// with those syntax rules.
    /// </summary>
    void setURI(const QString& value);

    /// <summary>
    /// nestedPackages getter
    /// References the packaged elements that are Packages.
    /// </summary>
    vector<shared_ptr<Package>>& nestedPackages();

    /// <summary>
    /// nestingPackage getter
    /// References the Package that owns this Package.
    /// </summary>
    shared_ptr<Package> nestingPackage() const;

    /// <summary>
    /// nestingPackage setter
    /// References the Package that owns this Package.
    /// </summary>
    void setNestingPackage(shared_ptr<Package> value);

    /// <summary>
    /// ownedStereotypes getter
    /// References the Stereotypes that are owned by the Package.
    /// </summary>
    vector<shared_ptr<Stereotype>>& ownedStereotypes();

    /// <summary>
    /// ownedTypes getter
    /// References the packaged elements that are Types.
    /// </summary>
    vector<shared_ptr<Type>>& ownedTypes();

    /// <summary>
    /// packageMerges getter
    /// References the PackageMerges that are owned by this Package.
    /// </summary>
    vector<shared_ptr<PackageMerge>>& packageMerges();

    /// <summary>
    /// packagedElements getter
    /// Specifies the packageable elements that are owned by this Package.
    /// </summary>
    vector<shared_ptr<PackageableElement>>& packagedElements();

    /// <summary>
    /// profileApplications getter
    /// References the ProfileApplications that indicate which profiles have been
    /// applied to the Package.
    /// </summary>
    vector<shared_ptr<ProfileApplication>>& profileApplications();

private: // Fields
    /// <summary>
    /// URI
    /// Provides an identifier for the package that can be used for many purposes. A URI
    /// is the universally unique identification of the package following the IETF URI
    /// specification, RFC 2396 http://www.ietf.org/rfc/rfc2396.txt and it must comply
    /// with those syntax rules.
    /// </summary>
    const static QString URI_default_;
    QString URI_= URI_default_;

    /// <summary>
    /// nestedPackages
    /// References the packaged elements that are Packages.
    /// </summary>
    vector<shared_ptr<Package>> nestedPackages_;

    /// <summary>
    /// nestingPackage
    /// References the Package that owns this Package.
    /// </summary>
    shared_ptr<Package> nestingPackage_;

    /// <summary>
    /// ownedStereotypes
    /// References the Stereotypes that are owned by the Package.
    /// </summary>
    vector<shared_ptr<Stereotype>> ownedStereotypes_;

    /// <summary>
    /// ownedTypes
    /// References the packaged elements that are Types.
    /// </summary>
    vector<shared_ptr<Type>> ownedTypes_;

    /// <summary>
    /// packageMerges
    /// References the PackageMerges that are owned by this Package.
    /// </summary>
    vector<shared_ptr<PackageMerge>> packageMerges_;

    /// <summary>
    /// packagedElements
    /// Specifies the packageable elements that are owned by this Package.
    /// </summary>
    vector<shared_ptr<PackageableElement>> packagedElements_;

    /// <summary>
    /// profileApplications
    /// References the ProfileApplications that indicate which profiles have been
    /// applied to the Package.
    /// </summary>
    vector<shared_ptr<ProfileApplication>> profileApplications_;

};

} // namespace uml
